package com.small.nacos.naming.core;

import com.alibaba.nacos.api.exception.NacosException;
import com.small.nacos.naming.misc.UtilsAndCommons;
import org.springframework.stereotype.Component;

import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentSkipListMap;
import java.util.concurrent.CopyOnWriteArrayList;


/**
 * @Author zhoujin
 * @Date 2022/7/22 20:53
 */
@Component
public class ServiceManager {

    Map<String, Map<String, Service>> serviceMap = new ConcurrentHashMap();

    private final Object LOCK = new Object();

    public void registerInstance(String namespaceId, String serviceName, Instance instance) throws NacosException {
        //创建service
        createEmptyService(namespaceId, serviceName, instance.isEphemeral());

        //添加实例
        addInstance(namespaceId, serviceName, instance.isEphemeral(), instance);

    }

    private void addInstance(String namespaceId, String serviceName, boolean ephemeral, Instance... instances) {
        Service service = getService(namespaceId, serviceName);
        synchronized (service) {
            updateIpAddresses(service, UtilsAndCommons.UPDATE_INSTANCE_ACTION_ADD, ephemeral, instances);
        }
    }

    private void updateIpAddresses(Service service, String action, boolean ephemeral, Instance... instances) {

        for (Instance instance : instances) {
            if (!service.getClusterMap().containsKey(instance.getClusterName())) {
                Cluster cluster = new Cluster(instance.getClusterName(), service);
                service.getClusterMap().put(instance.getClusterName(), cluster);
            }

            Cluster cluster = service.getClusterMap().get(instance.getClusterName());
            List<Instance> updateInstances = cluster.getIPs(ephemeral);
            HashMap<String, Instance> instanceMap = new HashMap();

            for (Instance update : updateInstances) {
                instanceMap.put(update.getDatumKey(), update);
            }

            if (UtilsAndCommons.UPDATE_INSTANCE_ACTION_REMOVE.equals(action)) {
                instanceMap.remove(instance.getDatumKey());
            } else {
                instanceMap.put(instance.getDatumKey(), instance);
            }
            CopyOnWriteArrayList<Instance> updateIps = new CopyOnWriteArrayList<>(instanceMap.values());
            cluster.updateIps(updateIps, ephemeral);
        }

    }


    public void createEmptyService(String namespaceId, String serviceName, boolean local) throws NacosException {
        createServiceIfAbsent(namespaceId, serviceName, local, null);
    }

    public void createServiceIfAbsent(String namespaceId, String serviceName, boolean local, Cluster cluster)
            throws NacosException {

        if (getService(namespaceId, serviceName) != null) {
            return;
        }
        Service service = new Service();
        service.setName(serviceName);
        service.setNamespaceId(namespaceId);
        service.setLastModifiedMillis(System.currentTimeMillis());
        putServiceAndInit(service);

    }

    private void putServiceAndInit(Service service) throws NacosException {
        putService(service);
    }

    public void putService(Service service) {
        if (!serviceMap.containsKey(service.getNamespaceId())) {
            synchronized (LOCK) {
                if (!serviceMap.containsKey(service.getNamespaceId())) {
                    serviceMap.put(service.getNamespaceId(), new ConcurrentSkipListMap<>());
                }
            }
        }
        serviceMap.get(service.getNamespaceId()).putIfAbsent(service.getName(), service);
    }

    public Service getService(String namespaceId, String serviceName) {
        if (chooseServiceMap(namespaceId) != null) {
            return chooseServiceMap(namespaceId).get(serviceName);
        }
        return null;
    }

    public Map<String, Service> chooseServiceMap(String namespaceId) {
        return serviceMap.get(namespaceId);
    }

    public void removeInstance(String namespaceId, String serviceName, boolean ephemeral, Instance ips) throws NacosException {

        Service service = getService(namespaceId, serviceName);

        synchronized (service) {
            substractIpAddresses(service, ephemeral, ips);
        }
    }

    private void substractIpAddresses(Service service, boolean ephemeral, Instance... ips)
            throws NacosException {
        updateIpAddresses(service, UtilsAndCommons.UPDATE_INSTANCE_ACTION_REMOVE, ephemeral, ips);
    }

    public Instance getInstance(String namespaceId, String serviceName, String cluster, String ip, int port, Boolean ephemeral) {
        Service service = getService(namespaceId, serviceName);
        if (service == null) {
            return null;
        }

        List<Instance> ips = service.allIPs(Arrays.asList(cluster), ephemeral);
        if (ips == null || ips.isEmpty()) {
            return null;
        }

        for (Instance instance : ips) {
            if (instance.getIp().equals(ip) && instance.getPort() == port) {
                return instance;
            }
        }

        return null;
    }
}
